#pragma once

#include <iostream>
#include <string>
#include <unordered_map>

#include <functional>
#include <memory>

#include "../Socket.hpp"
#include "../epoller.hpp"
#include "../nocopy.hpp"
#include "../Comm.hpp"

class Connection;
class TcpServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>;

static const uint16_t defaultport = 8082;
static const int defaulttimeout = 3000;
static const int buffer_size = 128;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);

class Connection
{
public:
    Connection(const int sockfd, std::shared_ptr<TcpServer> tcp_server_ptr, const std::string &ip, const uint16_t port)
        : _sockfd(sockfd), _tcp_server_ptr(tcp_server_ptr), _ip(ip), _port(port)
    {
    }
    void SetCallBack(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    int GetSockfd()
    {
        return _sockfd;
    }

    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }
    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }

private:
    int _sockfd;

public:
    std::string _inbuffer;  // 输入缓冲区
    std::string _outbuffer; // 输出缓冲区

    // 回指指针
    std::shared_ptr<TcpServer> _tcp_server_ptr;

    func_t _recv_cb;   // 读取数据的回调函数
    func_t _send_cb;   // 写入数据的回调函数
    func_t _except_cb; // 操作异常的回调函数

    std::string _ip;
    uint16_t _port;
};

class TcpServer : public Nocopy
{
public:
    TcpServer(func_t OnMessage, const uint16_t port = defaultport, const int timeout = defaulttimeout)
        : _epoller_ptr(new Epoller()), _listen_sockfd(new Sock()), _port(port), _timeout(timeout), _OnMessage(OnMessage)
    {
    }
    void Init()
    {
        _listen_sockfd->Socket();
        SetNonBlockOrDie(_listen_sockfd->Fd());
        _listen_sockfd->Bind(_port);
        _listen_sockfd->Listen();
        AddConnection(_listen_sockfd->Fd(), EVENT_IN,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1),
                      nullptr, nullptr, "0.0.0.0", _port);
    }
    void AddConnection(int sockfd, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb, const std::string &ip, const uint16_t port)
    {
        // 1. 创建Connection对象
        std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sockfd, std::shared_ptr<TcpServer>(this), ip, port);
        new_connection->SetCallBack(recv_cb, send_cb, except_cb);
        // 2. 添加到unordered_map中
        _connections.insert(std::make_pair(sockfd, new_connection));
        // 3. 添加事件到epoll模型中
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sockfd, event);
    }
    void Start()
    {
        while (true)
        {
            int n = _epoller_ptr->EpollerWait(revs, num, _timeout);
            if (n < 0)
            {
                std::cerr << "epoll_wait fail" << std::endl;
                exit(-1);
            }
            else if (n == 0)
            {
                std::cout << "time out" << std::endl;
                Print();
            }
            else
            {
                HandlerEvent(n);
            }
        }
    }
    bool IsConnectionSafe(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        if (iter == _connections.end())
            return false;
        return true;
    }
    void HandlerEvent(int n)
    {
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events;
            int sockfd = revs[i].data.fd;
            // 统一把事件异常转换为读写问题，当事件异常时，在读写时也会出错，就不用单独处理异常问题了
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLIN && IsConnectionSafe(sockfd))
            {
                if (_connections[sockfd]->_recv_cb)
                    _connections[sockfd]->_recv_cb(_connections[sockfd]);
            }
            if (events & EPOLLOUT && IsConnectionSafe(sockfd))
            {
                if (_connections[sockfd]->_send_cb)
                    _connections[sockfd]->_send_cb(_connections[sockfd]);
            }
        }
    }
    // 链接处理器
    void Accepter(std::shared_ptr<Connection> connection)
    {
        while (true)
        {
            struct sockaddr_in client;
            memset(&client, 0, sizeof(client));
            socklen_t len = sizeof(client);

            int sockfd = ::accept(connection->GetSockfd(), (struct sockaddr *)&client, &len);
            if (sockfd > 0)
            {
                uint16_t clientport = ntohs(client.sin_port);
                char ipbuffer[64];
                std::string clientip = inet_ntop(AF_INET, &client.sin_addr, ipbuffer, sizeof(ipbuffer));
                std::cout << "[Info][get a new link...][clientip:" << clientip << " clientport:" << clientport << "]" << std::endl;

                SetNonBlockOrDie(sockfd);
                AddConnection(sockfd, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1), clientip, clientport);
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                    break;
            }
        }
    }
    // 事件处理器
    void Recver(std::shared_ptr<Connection> connection)
    {
        while (true)
        {
            char read_buffer[buffer_size];
            ssize_t n = recv(connection->GetSockfd(), read_buffer, sizeof(read_buffer) - 1, 0);
            if (n > 0)
            {
                read_buffer[n] = '\0';
                connection->AppendInBuffer(read_buffer);
            }
            else if (n == 0)
            {
                std::cerr << "[Debug][client quit][clientip:" << connection->_ip << " clientport:" << connection->_port << "]" << std::endl;
                connection->_except_cb(connection);
                break;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    std::cerr << "read fail" << std::endl;
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        // 上层处理数据,用户层协议内容
        _OnMessage(connection);
    }
    void Sender(std::shared_ptr<Connection> connection)
    {
        std::string message = connection->_outbuffer;
        while (true)
        {
            ssize_t n = send(connection->GetSockfd(), message.c_str(), message.size(), 0);
            if (n > 0)
            {
                message.erase(0, n);
                if (message.empty())
                    break;
            }
            else if (n == 0)
            {
                // 上层数据发完了
                return;
            }
            else
            {
                // 底层缓冲区不够了
                if (errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                    continue;
                else
                {
                    std::cerr << "send fail" << std::endl;
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        if(!message.empty())
        {
            //开启对写事件的关系
            EnableEvent(connection->GetSockfd(), true, true);
        }
        else
        {
            //关闭对写事件的关心
            EnableEvent(connection->GetSockfd(), true, false);
        }
    }
    void Excepter(std::shared_ptr<Connection> connection)
    {
        //移除epoll对事件的关心
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, connection->GetSockfd(), 0);
        //关闭异常的文件描述符
        close(connection->GetSockfd());
        //移除unordered_map中的记录
        //不能直接移除，会有double free的问题
        //_connections.erase(connection->GetSockfd());
    }
    void EnableEvent(int sockfd, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sockfd, events);
    }
    void Print()
    {
        for (auto &connection : _connections)
        {
            std::cout << connection.first << " ";
            std::cout << connection.second->_inbuffer << std::endl;
        }
        std::cout << std::endl;
    }

    ~TcpServer()
    {
    }

private:
    // 整个必须时static const这样才不属于单独的类
    // 如何使const的话，不同的类通过初始化列表后值可能会不一样
    static const int num = 64;

    // epoll模型指针和监听套接字指针
    std::shared_ptr<Epoller> _epoller_ptr;
    std::shared_ptr<Sock> _listen_sockfd;

    uint16_t _port;

    // 文件描述符和Connection结构体的映射
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    struct epoll_event revs[num];
    const int _timeout; // epoll模型等待超时时间

    // 让上层处理信息
    func_t _OnMessage;
};